﻿using System;
using System.Collections;
using System.Threading;
using System.Web;
using System.IO;
using System.Configuration;
using System.Diagnostics;

using AppModule.InterProcessComm;
using AppModule.NamedPipes;

namespace WinCommand.CommandPipe
{

    public sealed class PipeManager : IChannelManager
    {
        private const int MAX_READ_BYTES = 5000;
        private const int PIPE_MAX_STUFFED_TIME = 5000;

        private Hashtable _Pipes;

        private uint _NumberPipes = 5;
        private uint _OutBuffer = 512;
        private uint _InBuffer = 512;
        
        private bool _Listen = true;
        private int _NumChannels = 0;
        private Hashtable _InternalPipes;
        private Thread _MainThread;
        private string _PipeName = "WinCommandPipe";
        private ManualResetEvent _Mre;
        
        public object SyncRoot = new object();

        public delegate void PipeEventHandler(object sender, PipeEventArgs e);
        public event PipeEventHandler PipeEvent;

        /// <summary>
        /// Initializes a new instance of the PipeManager class.
        /// </summary>
        public PipeManager()
        {
            _InternalPipes = new Hashtable();
        }

        public Hashtable Pipes {
            get {
                return _Pipes;
            }
        }

        private void OnPipeEvent(PipeEventArgs e)
        {
            if (PipeEvent != null)
                PipeEvent(this, e);
        }

        public void Initialize()
        {
            _Pipes = Hashtable.Synchronized(_InternalPipes);
            _Mre = new ManualResetEvent(false);
            _MainThread = new Thread(new ThreadStart(Start));
            _MainThread.IsBackground = true;
            _MainThread.Name = "Main Pipe Thread";
            _MainThread.Start();
            Thread.Sleep(1000);
        }

        public bool Listen
        {
            get
            {
                return _Listen;
            }
            set
            {
                _Listen = value;
            }
        }

        public string HandleRequest(string request)
        {
            string returnVal;

            OnPipeEvent(new PipeEventArgs(request, PipeEventArgs.PipeEvent.Message));

            returnVal = "Response to: " + request;

            return returnVal;
        }

        private void Start()
        {
            try
            {
                while (_Listen)
                {
                    int[] keys = new int[Pipes.Keys.Count];
                    Pipes.Keys.CopyTo(keys, 0);
                    foreach (int key in keys)
                    {
                        ServerNamedPipe serverPipe = (ServerNamedPipe)Pipes[key];
                        if (serverPipe != null && DateTime.Now.Subtract(serverPipe.LastAction).Milliseconds > PIPE_MAX_STUFFED_TIME && serverPipe.PipeConnection.GetState() != InterProcessConnectionState.WaitingForClient)
                        {
                            serverPipe.Listen = false;
                            serverPipe.PipeThread.Abort();
                            RemoveServerChannel(serverPipe.PipeConnection.NativeHandle);
                        }
                    }
                    if (_NumChannels <= _NumberPipes)
                    {
                        ServerNamedPipe pipe = new ServerNamedPipe(_PipeName, _OutBuffer, _InBuffer, MAX_READ_BYTES, false, this);
                        pipe.PipeEvent += new ServerNamedPipe.PipeEventHandler(pipe_PipeEvent);
                        try
                        {
                            pipe.Connect();
                            pipe.LastAction = DateTime.Now;
                            System.Threading.Interlocked.Increment(ref _NumChannels);
                            pipe.Start();
                            Pipes.Add(pipe.PipeConnection.NativeHandle, pipe);
                        }
                        catch (InterProcessIOException ex)
                        {
                            RemoveServerChannel(pipe.PipeConnection.NativeHandle);
                            pipe.Dispose();
                        }
                    }
                    else
                    {
                        _Mre.Reset();
                        _Mre.WaitOne(1000, false);
                    }
                }
            }
            catch
            {
                // Log exception
            }
        }

        void pipe_PipeEvent(object sender, PipeEventArgs e)
        {
            OnPipeEvent(e);
        }

        public void Stop()
        {
            _Listen = false;
            _Mre.Set();
            try
            {
                int[] keys = new int[Pipes.Keys.Count];
                Pipes.Keys.CopyTo(keys, 0);
                foreach (int key in keys)
                {
                    ((ServerNamedPipe)Pipes[key]).Listen = false;
                }
                int i = _NumChannels * 3;
                for (int j = 0; j < i; j++)
                {
                    StopServerPipe();
                }
                Pipes.Clear();
                _Mre.Close();
                _Mre = null;
            }
            catch
            {
                // Log exception
            }
        }

        public void WakeUp()
        {
            if (_Mre != null)
            {
                _Mre.Set();
            }
        }
        private void StopServerPipe()
        {
            try
            {
                ClientPipeConnection pipe = new ClientPipeConnection(_PipeName);
                if (pipe.TryConnect())
                {
                    pipe.Close();
                }
            }
            catch
            {
                // Log exception
            }
        }

        public void RemoveServerChannel(object param)
        {
            int handle = (int)param;
            System.Threading.Interlocked.Decrement(ref _NumChannels);
            Pipes.Remove(handle);
            this.WakeUp();
        }

    }
}